#pragma once
#pragma execution_character_set("utf-8")

#include <map>
using std::map;

#include <QtWidgets>

namespace {

int fontSize = 12;
int marginTop = 8;
int marginBottom = 8;
int marginLeft = 8;
int marginRight = 8;
int spacing = 4;

} // namespace

class QtGuiBuilder
{
public:
    QtGuiBuilder() {}
    ~QtGuiBuilder() = default;

    void done() { __done(); }

    // main window
    inline QtGuiBuilder& setMainWindow(QMainWindow* main) { return __setMainWindow(main); }

    // Dialog
    inline QtGuiBuilder& setDialog(QDialog* dialog) { return __setDialog(dialog); }

    inline QtGuiBuilder& setFontSize(QWidget* widget, int fontSize)
    {
        return __setFontSize(widget, fontSize);
    }

    inline QtGuiBuilder& setDialogSize(QWidget* widget, int charCount, int rowCount)
    {
        return __setDialogSize(widget, charCount, rowCount);
    }

    // menu bar
    inline QtGuiBuilder& setMenuBar(QMenuBar* menubar) { return __setMenuBar(menubar); }
    inline QtGuiBuilder& addMenu(const QString& title, const QString& name = "")
    {
        return __addMenu(title, name);
    }
    inline QtGuiBuilder& addMenuAction(
        QAction* action,
        std::function<void(QAction*)> postAction = nullptr
    )
    {
        return __addMenuAction(action, postAction);
    }
    inline QtGuiBuilder& addMenuSeparator() { return __addMenuSeparator(); }

    // toolbar
    inline QtGuiBuilder& addToolBar(
        QWidget* parent,
        std::function<void(QToolBar*)> postAction = nullptr
    )
    {
        return __addToolBar(parent, postAction);
    }
    inline QtGuiBuilder& setToolBar(QToolBar* toolbar) { return __setToolBar(toolbar); }
    inline QtGuiBuilder& addToolBarAction(
        QAction* action,
        std::function<void(QAction*)> postAction = nullptr
    )
    {
        return __addToolBarAction(action, postAction);
    }
    inline QtGuiBuilder& addToolBarAction(
        const QString& title,
        const QIcon& icon,
        std::function<void(QAction*)> postAction = nullptr
    )
    {
        return __addToolBarAction(title, icon, postAction);
    }
    inline QtGuiBuilder& addToolBarSeparator() { return __addToolBarSeparator(); }

    // status bar
    inline QtGuiBuilder& setStatusBar(QStatusBar* statusBar)
    {
        return __setStatusBar(statusBar);
    }
    inline QtGuiBuilder& addStatusBarLabel(const QString& key, const QString& text = "")
    {
        return __addStatusBarLabel(key, text);
    }
    inline QtGuiBuilder& addStatusBarIcon(
        const QString& key,
        const QIcon& icon,
        const QString& text = ""
    )
    {
        return __addStatusBarIcon(key, icon, text);
    }
    inline QtGuiBuilder& showStatusBarMessage(const QString& message, int timeout = 10 * 1000)
    {
        return __showStatusBarMessage(message, timeout);
    }
    inline QtGuiBuilder& setStatusBarLabel(
        const QString& key,
        const QString& text,
        QColor color = Qt::black
    )
    {
        return __setStatusBarLabel(key, text, color);
    }

    // dock widget
    inline QtGuiBuilder& addDockWidget(
        Qt::DockWidgetArea area,
        const QString& title,
        QWidget* widget
    )
    {
        return __addDockWidget(area, title, widget);
    }

    // central widget
    inline QtGuiBuilder& useCentralWidget(QMainWindow* window)
    {
        return __useCentralWidget(window);
    }

    // mdi area
    inline QtGuiBuilder& setMdiArea(QMainWindow* window) { return __setMdiArea(window); }
    inline QtGuiBuilder& addMdiSubWindow(QWidget* widget, const QString& title = "")
    {
        return __addMdiSubWindow(widget, title);
    }

    // layout
    inline QtGuiBuilder& setGridLayout(QWidget* widget) { return __setGridLayout(widget); }
    inline QtGuiBuilder& setHBoxLayout(
        QWidget* widget,
        std::function<void(QHBoxLayout*)> postAction = nullptr
    )
    {
        return __setHBoxLayout(widget, postAction);
    }
    inline QtGuiBuilder& setVBoxLayout(
        QWidget* widget,
        std::function<void(QVBoxLayout*)> postAction = nullptr
    )
    {
        return __setVBoxLayout(widget, postAction);
    }
    inline QtGuiBuilder& setFormLayout(QWidget* widget) { return __setFormLayout(widget); }
    inline QtGuiBuilder& vBoxAddWidget(
        QWidget* widget,
        std::function<void(QWidget*)> postAction = nullptr
    )
    {
        return __vBoxAddWidget(widget, postAction);
    }
    inline QtGuiBuilder& hBoxAddWidget(
        QWidget* widget,
        std::function<void(QWidget*)> postAction = nullptr
    )
    {
        return __hBoxAddWidget(widget, postAction);
    }
    inline QtGuiBuilder& withGridLayout(std::function<void(QGridLayout*)> postAction = nullptr)
    {
        return __withGridLayout(postAction);
    }
    inline QtGuiBuilder& withVBoxLayout(std::function<void(QVBoxLayout*)> postAction = nullptr)
    {
        return __withVBoxLayout(postAction);
    }
    inline QtGuiBuilder& withHBoxLayout(std::function<void(QHBoxLayout*)> postAction = nullptr)
    {
        return __withHBoxLayout(postAction);
    }
    inline QtGuiBuilder& withFormLayout(std::function<void(QFormLayout*)> postAction = nullptr)
    {
        return __withFormLayout(postAction);
    }

    // tab widget
    inline QtGuiBuilder& setTabWidget(QTabWidget* tabWidget)
    {
        return __setTabWidget(tabWidget);
    }
    inline QtGuiBuilder& addTabWidget(std::function<void(QTabWidget*)> postAction = nullptr)
    {
        return __addTabWidget(postAction);
    }
    inline QtGuiBuilder& addTab(QWidget* widget, const QString& title)
    {
        return __addTab(widget, title);
    }

private:
    // main window
    inline QtGuiBuilder& __setMainWindow(QMainWindow* main)
    {
        main_ = main;
        return *this;
    }

    // Dialog
    inline QtGuiBuilder& __setDialog(QDialog* dialog)
    {
        dialog_ = dialog;
        return *this;
    }

    inline QtGuiBuilder& __setFontSize(QWidget* widget, int fontSize)
    {
        QFont font = widget->font();
        font.setPointSize(fontSize);
        widget->setFont(font);

        // widget->setMinimumSize(fontWidth, fontHeight);
        return *this;
    }

    inline QtGuiBuilder& __setDialogSize(QWidget* widget, int charCount, int rowCount)
    {
        widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
#ifdef _WIN32
        int fontWidth = widget->fontMetrics().maxWidth();
#else
        int fontWidth = widget->fontMetrics().averageCharWidth();
#endif

        int fontHeight = widget->fontMetrics().height();
        widget->setMinimumSize(
            charCount * fontWidth + marginLeft + marginRight,
            rowCount * (fontHeight + 4) + rowCount * spacing + marginTop + marginBottom
        );

        return *this;
    }

    inline QtGuiBuilder& __setMenuBar(QMenuBar* menubar)
    {
        menubar_ = menubar;
        // if (main_ != nullptr) {
        //     menubar_->setFont(main_->font());
        // }
        return *this;
    }

    inline QtGuiBuilder& __addMenu(const QString& title, const QString& name)
    {
        menu_ = new QMenu(menubar_);
        if (!name.isEmpty()) {
            menu_->setObjectName(name);
        }
        menu_->setTitle(title);
        menu_->setFont(menubar_->font());
        menubar_->addAction(menu_->menuAction());
        return *this;
    }

    inline QtGuiBuilder& __addMenuAction(
        QAction* action,
        std::function<void(QAction*)> postAction
    )
    {
        menu_->addAction(action);
        if (postAction) {
            postAction(action);
        }
        return *this;
    }

    inline QtGuiBuilder& __addMenuSeparator()
    {
        menu_->addSeparator();
        return *this;
    }

    inline QtGuiBuilder& __addToolBar(
        QWidget* parent,
        std::function<void(QToolBar*)> postAction
    )
    {
        toolbar_ = new QToolBar(parent);
        main_->addToolBar(Qt::TopToolBarArea, toolbar_);
        if (postAction) {
            postAction(toolbar_);
        }
        return *this;
    }

    inline QtGuiBuilder& __setToolBar(QToolBar* toolbar)
    {
        toolbar_ = toolbar;
        return *this;
    }

    inline QtGuiBuilder& __addToolBarAction(
        QAction* action,
        std::function<void(QAction*)> postAction = nullptr
    )
    {
        toolbar_->addAction(action);
        if (postAction) {
            postAction(action);
        }
        return *this;
    }

    inline QtGuiBuilder& __addToolBarAction(
        const QString& title,
        const QIcon& icon,
        std::function<void(QAction*)> postAction
    )
    {
        QAction* action = new QAction(title, toolbar_);
        action->setIcon(icon);
        toolbar_->addAction(action);

        if (postAction) {
            postAction(action);
        }
        return *this;
    }

    inline QtGuiBuilder& __addToolBarSeparator()
    {
        toolbar_->addSeparator();
        return *this;
    }

    inline QtGuiBuilder& __setStatusBar(QStatusBar* statusBar)
    {
        statusBar_ = statusBar;
        return *this;
    }

    inline QtGuiBuilder& __addStatusBarLabel(const QString& key, const QString& text)
    {
        QLabel* label = new QLabel(text, statusBar_);
        statusBar_->addPermanentWidget(label);
        statusBarLabels_[key] = label;
        return *this;
    }

    inline QtGuiBuilder& __addStatusBarIcon(
        const QString& key,
        const QIcon& icon,
        const QString& text
    )
    {
        QLabel* label = new QLabel(text, statusBar_);

        // TODO: need use 1 widget for both icon and text
        QLabel* labelPixmap = new QLabel(statusBar_);
        labelPixmap->setPixmap(icon.pixmap(16, 16));
        statusBar_->addPermanentWidget(labelPixmap);
        statusBar_->addPermanentWidget(label);
        statusBarLabels_[key] = label;
        return *this;
    }

    inline QtGuiBuilder& __showStatusBarMessage(const QString& message, int timeout = 10 * 1000)
    {
        if (statusBar_ != nullptr) {
            statusBar_->showMessage(message, timeout);
        }
        return *this;
    }

    inline QtGuiBuilder& __setStatusBarLabel(
        const QString& key,
        const QString& text,
        QColor color = Qt::black
    )
    {
        if (statusBarLabels_.count(key) > 0) {
            statusBarLabels_[key]->setText(text);
            statusBarLabels_[key]->setStyleSheet("color: " + color.name() + ";");
        }
        return *this;
    }

    inline QtGuiBuilder& __useCentralWidget(QMainWindow* window)
    {
        if (window->centralWidget() == nullptr) {
            window->setCentralWidget(new QWidget(window));
        }
        return *this;
    }

    inline QtGuiBuilder& __setMdiArea(QMainWindow* main)
    {
        mdiArea_ = new QMdiArea(main);
        main->setCentralWidget(mdiArea_);
        mdiArea_->setViewMode(QMdiArea::TabbedView);
        mdiArea_->setTabsClosable(true);
        mdiArea_->setTabsMovable(true);
        return *this;
    }

    inline QtGuiBuilder& __addMdiSubWindow(QWidget* widget, const QString& title)
    {
        QMdiSubWindow* window = mdiArea_->addSubWindow(widget);
        window->setWindowTitle(title);
        widget->showMaximized();
        return *this;
    }

    inline QtGuiBuilder& __setGridLayout(QWidget* widget)
    {
        gridLayout_ = new QGridLayout(widget);
        widget->setLayout(gridLayout_);
        return *this;
    }

    inline QtGuiBuilder& __setHBoxLayout(
        QWidget* widget,
        std::function<void(QHBoxLayout*)> postAction
    )
    {
        hBoxLayout_ = new QHBoxLayout(widget);
        widget->setLayout(hBoxLayout_);
        if (postAction) {
            postAction(hBoxLayout_);
        }
        return *this;
    }

    inline QtGuiBuilder& __setVBoxLayout(
        QWidget* widget,
        std::function<void(QVBoxLayout*)> postAction
    )
    {
        vBoxLayout_ = new QVBoxLayout(widget);
        // widget->setLayout(vBoxLayout_);
        if (postAction) {
            postAction(vBoxLayout_);
        }
        return *this;
    }

    inline QtGuiBuilder& __setFormLayout(QWidget* widget)
    {
        formLayout_ = new QFormLayout(widget);
        widget->setLayout(formLayout_);
        return *this;
    }

    inline QtGuiBuilder& __vBoxAddWidget(
        QWidget* widget,
        std::function<void(QWidget*)> postAction
    )
    {
        vBoxLayout_->addWidget(widget);
        currentWidget_ = widget;
        if (postAction) {
            postAction(widget);
        }
        return *this;
    }

    inline QtGuiBuilder& __hBoxAddWidget(
        QWidget* widget,
        std::function<void(QWidget*)> postAction
    )
    {
        hBoxLayout_->addWidget(widget);
        currentWidget_ = widget;
        if (postAction) {
            postAction(widget);
        }
        return *this;
    }

    inline QtGuiBuilder& __withGridLayout(std::function<void(QGridLayout*)> postAction)
    {
        auto layout = new QGridLayout(currentWidget_);

        if (postAction) {
            postAction(layout);
        }
        return *this;
    }

    inline QtGuiBuilder& __withVBoxLayout(std::function<void(QVBoxLayout*)> postAction)
    {
        auto layout = new QVBoxLayout(currentWidget_);

        if (postAction) {
            postAction(layout);
        }
        return *this;
    }

    inline QtGuiBuilder& __withHBoxLayout(std::function<void(QHBoxLayout*)> postAction)
    {
        auto layout = new QHBoxLayout(currentWidget_);

        if (postAction) {
            postAction(layout);
        }
        return *this;
    }

    inline QtGuiBuilder& __withFormLayout(std::function<void(QFormLayout*)> postAction)
    {
        auto layout = new QFormLayout(currentWidget_);

        if (postAction) {
            postAction(layout);
        }
        return *this;
    }

    inline QtGuiBuilder& __setTabWidget(QTabWidget* tabWidget)
    {
        tabWidget_ = tabWidget;
        return *this;
    }

    inline QtGuiBuilder& __addTabWidget(std::function<void(QTabWidget*)> postAction)
    {
        tabWidget_ = new QTabWidget(gridLayout_->parentWidget());
        tabWidget_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
        gridLayout_->addWidget(tabWidget_, 0, 0);

        if (postAction) {
            postAction(tabWidget_);
        }
        return *this;
    }
    inline QtGuiBuilder& __addTab(QWidget* widget, const QString& title)
    {
        QWidget* tab = new QWidget(tabWidget_);
        tabWidget_->addTab(tab, title);

        QGridLayout* layout = new QGridLayout(tab);
        layout->addWidget(widget, 0, 0);

        return *this;
    }

    inline QtGuiBuilder& __addDockWidget(
        Qt::DockWidgetArea area,
        const QString& title,
        QWidget* widget
    )
    {
        QDockWidget* dock = new QDockWidget(main_);
        dock->setWindowTitle(title);
        dock->setWidget(widget);

        main_->addDockWidget(area, dock);
        return *this;
    }

    inline void __done()
    {
        main_ = nullptr;
        dialog_ = nullptr;
        menubar_ = nullptr;
        menu_ = nullptr;
        toolbar_ = nullptr;
        statusBar_ = nullptr;

        mdiArea_ = nullptr;
        gridLayout_ = nullptr;
        hBoxLayout_ = nullptr;
        vBoxLayout_ = nullptr;
        formLayout_ = nullptr;
        tabWidget_ = nullptr;
        currentWidget_ = nullptr;
    }

private:
    QMainWindow* main_ = nullptr;
    QDialog* dialog_ = nullptr;
    QMenuBar* menubar_ = nullptr;
    QMenu* menu_ = nullptr;
    QToolBar* toolbar_ = nullptr;
    QStatusBar* statusBar_ = nullptr;
    map<QString, QLabel*> statusBarLabels_;
    QMdiArea* mdiArea_ = nullptr;
    QGridLayout* gridLayout_ = nullptr;
    QHBoxLayout* hBoxLayout_ = nullptr;
    QVBoxLayout* vBoxLayout_ = nullptr;
    QFormLayout* formLayout_ = nullptr;
    QTabWidget* tabWidget_ = nullptr;
    QWidget* currentWidget_ = nullptr;
};
